package top.bug.blog.aop;

import cn.hutool.json.JSONUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import top.bug.blog.entity.po.SysOperationLog;
import top.bug.blog.enums.ErrorCode;
import top.bug.blog.exception.BussinessException;
import top.bug.blog.service.OperationLogService;
import top.bug.blog.utils.DesensitizationUtil;
import top.bug.blog.utils.IpUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

/**
 * @author <a href="https://github.com/jiusi66">九思.</a>
 * @email 10837907@qq.com
 * @from <a href="https://www.52bug.top">递归茶馆-www.52bug.top</a>
 * @description 后台日志拦截器
 */
@Aspect
@Component
@Slf4j
public class OperationLogAspect {

    @Resource
    private OperationLogService operationLogService;

    /**
     * 切入点：后台接口
     */
    @Pointcut("execution(* top.bug.blog.controller.backend..*.*(..))")
    public void operationLogPointCut() {
    }

    @Around("operationLogPointCut()")
    public Object recordOperationLog(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = null;
        SysOperationLog operationLog = new SysOperationLog();

        try {
            // 1. 获取请求信息
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();

            // 2. 获取Knife4j注解信息
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();

            // 3. 从类注解获取模块名称
            Api classAnnotation = method.getDeclaringClass().getAnnotation(Api.class);
            if (classAnnotation != null && classAnnotation.tags().length > 0) {
                // 取第一个tag作为模块名
                operationLog.setModule(classAnnotation.tags()[0]);
            }

            // 4. 从方法注解获取操作描述
            ApiOperation methodAnnotation = method.getAnnotation(ApiOperation.class);
            if (methodAnnotation != null) {
                operationLog.setDescription(methodAnnotation.value());
            }

            // 5. 设置基础信息
            operationLog.setRequestUrl(request.getRequestURI());
            operationLog.setRequestMethod(request.getMethod());
            operationLog.setIpAddress(IpUtils.getClientIp(request));

            // 6. 记录请求参数
            Object[] args = joinPoint.getArgs();
            if (args != null && args.length > 0) {
                String reqStr = JSONUtil.toJsonStr(args);
                operationLog.setRequestParams(reqStr.length() > 2000 ? "REQUEST_TOO_LARGE" :
                        DesensitizationUtil.desensitizeJson(reqStr));
            }

            // 7. 执行目标方法
            result = joinPoint.proceed();

            // 8. 记录响应结果
            if (result != null) {
                String responseJson = JSONUtil.toJsonStr(result);
                // 限制响应结果长度（避免过大字段）
                operationLog.setResponseResult(responseJson.length() > 2000 ?
                        "RESPONSE_TOO_LARGE" : DesensitizationUtil.desensitizeJson(responseJson));
            }
            // 操作成功
            operationLog.setStatus("1");

        } catch (Exception e) {
            // 操作失败
            if (e instanceof BussinessException) {
                Map<String, Object> errorMap = new HashMap<>(2);
                ErrorCode errorCode = ((BussinessException) e).getCode();
                errorMap.put("code", errorCode.getCode());
                errorMap.put("message", e.getMessage());
                operationLog.setResponseResult(JSONUtil.toJsonStr(errorMap));
            }
            operationLog.setStatus("0");
            operationLog.setErrorMessage(e.getMessage());
            throw e;
        } finally {
            // 9. 记录执行时间
            long endTime = System.currentTimeMillis();
            operationLog.setExecuteTime(endTime - startTime);

            // 10. 设置创建信息
            operationLog.setCreateOn(LocalDateTime.now());
            operationLog.setUpdateOn(LocalDateTime.now());

            // 11. 异步保存日志
            operationLogService.saveAsync(operationLog);
        }
        return result;
    }
}